﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using PSE.Tools.SchemaComparer.Extractor;
using System.Diagnostics;
using System.Threading;
using System.IO;

namespace Sinacor.Infra.Tools.SchemaExtractor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        internal class ExtractParameter
        {
            public string CompanyName
            {
                get;
                set;
            }

            public string DataSource
            {
                get;
                set;
            }

            public string UserName
            {
                get;
                set;
            }

            public string Password
            {
                get;
                set;
            }
        }

        private DBSchema _dbSchema;
        private BackgroundWorker _bgExtractAllSchema;

        private string _directory;
        private string _dataSource;
        private string _baseFileName = "DBSchema";
        private bool _zipFiles;
        private bool _pwdCryptographed;
        private StringBuilder _executionErrors;


        public List<Entity.Company> Companies
        {
            get { return (List<Entity.Company>)GetValue(CompaniesProperty); }
            set { SetValue(CompaniesProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Companies.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CompaniesProperty =
            DependencyProperty.Register("Companies", typeof(List<Entity.Company>), typeof(MainWindow), new UIPropertyMetadata(null));

        private ManualResetEvent _working;

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            this.Closing += new CancelEventHandler(MainWindow_Closing);
            _working = new ManualResetEvent(false);
            this.lstCompanies.SelectionChanged += new SelectionChangedEventHandler(lstCompanies_SelectionChanged);
        }

        void lstCompanies_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lstCompanies.SelectedIndex >= 0)
                lstCompanies.SelectedIndex = -1;
        }

        void MainWindow_Closing(object sender, CancelEventArgs e)
        {
            if (_working.WaitOne(0))
            {
                if (MessageBox.Show("Atenção, a extração de Schemas ainda não foi finalizada. Deseja realmente finalizar a aplicação ?", "", MessageBoxButton.YesNo) == MessageBoxResult.No)
                    e.Cancel = true;
                else
                {
                    if (_bgExtractAllSchema != null && _bgExtractAllSchema.IsBusy)
                    {
                        _bgExtractAllSchema.CancelAsync();

                        if (_dbSchema != null)
                            _dbSchema.Abort();
                    }
                }
            }

        }

        private void btnGetCompanies_Click(object sender, RoutedEventArgs e)
        {
            ExtractParameter parm = new ExtractParameter();
            StringBuilder erroMessage = new StringBuilder();

            parm.DataSource = txtDataSource.Text.Trim();
            parm.UserName = txtUserName.Text.Trim();
            parm.Password = txtPassword.Password.Trim();

            if (string.IsNullOrEmpty(parm.DataSource))
                erroMessage.AppendLine("- Data Source deve ser informado");

            if (string.IsNullOrEmpty(parm.UserName))
                erroMessage.AppendLine("- User Name deve ser informado");

            if (string.IsNullOrEmpty(parm.Password))
                erroMessage.AppendLine("- Password deve ser informado");

            if (erroMessage.Length > 0)
            {
                MessageBox.Show(erroMessage.ToString());
                return;
            }

            ShowLoadingPanel("Recuperando empresas cadastradas");

            this.Companies = null;

            BackgroundWorker bgGetCompanies = new BackgroundWorker();
            bgGetCompanies.DoWork += new DoWorkEventHandler(bgGetCompanies_DoWork);
            bgGetCompanies.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgGetCompanies_RunWorkerCompleted);
            bgGetCompanies.RunWorkerAsync(parm);

        }

        void bgGetCompanies_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            HideLoadingPanel();

            if (this.Companies != null && this.Companies.Count > 0)
                gridExport.Visibility = Visibility.Visible;
            else
                gridExport.Visibility = Visibility.Collapsed;
        }

        void bgGetCompanies_DoWork(object sender, DoWorkEventArgs e)
        {
            GetCompanies(e.Argument as ExtractParameter);
        }

        private void GetCompanies(ExtractParameter extractParameter)
        {
            try
            {
                ResouceAccess.Company companyRa = new ResouceAccess.Company();

                DBConnectionInfo connInfo = new DBConnectionInfo()
                {
                    User = extractParameter.UserName,
                    Password = extractParameter.Password,
                    DataSource = extractParameter.DataSource
                };

                List<Entity.Company> ret = companyRa.GetCompanies(connInfo);

                Dispatcher.Invoke(new Action(delegate()
                    {
                        this.Companies = ret;
                    }));
            }
            catch (Exception ex)
            {
                string message = "Erro ao carregar as informações de Schema da Base de Dados." + Environment.NewLine + ex.Message;
                EventLog.WriteEntry("SinacorSchemaExtractor", message, EventLogEntryType.Error);
                MessageBox.Show(message);
                return;
            }
        }

        private void btnSelectDirectory_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.FolderBrowserDialog dialog = new System.Windows.Forms.FolderBrowserDialog();
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtDirectory.Text = dialog.SelectedPath;
            }
        }

        private void btnExtrairSchemas_Click(object sender, RoutedEventArgs e)
        {
            List<ExtractParameter> parms = new List<ExtractParameter>();
            StringBuilder erroMessage = new StringBuilder();
            _dataSource = txtDataSource.Text.Trim();
            _directory = txtDirectory.Text.Trim();
            _zipFiles = true;
            _pwdCryptographed = chkPasswordCifrado.IsChecked.GetValueOrDefault();

            StringBuilder sb = new StringBuilder();

            if (string.IsNullOrEmpty(_dataSource))
                sb.Append("- DataSource deve ser informado");

            if (string.IsNullOrEmpty(_directory))
                sb.Append("- Diretório de Saída deve ser informado");

            if (sb.Length > 0)
            {
                MessageBox.Show(sb.ToString());
                return;
            }

            if (!Directory.Exists(_directory))
                Directory.CreateDirectory(_directory);

            //adicionar a lista de parametros os dados da base de sistema
            parms.Add(new ExtractParameter()
            {
                CompanyName = string.Empty,
                DataSource = _dataSource,
                UserName = txtUserName.Text,
                Password = txtPassword.Password
            });

            //adicionar a lista de parametros os dados das bases de empresas
            foreach (Entity.Company company in Companies)
            {
                parms.Add(new ExtractParameter()
                {
                    CompanyName = company.Name,
                    DataSource = _dataSource,
                    UserName = company.UserName,
                    //se tiver sido indicado que o password esta cifrado na base, recuperar o pwd decifrado
                    Password = _pwdCryptographed ? company.GetDecryptedPassword() : company.Password,
                });
            }

            ShowLoadingPanel("");

            _bgExtractAllSchema = new BackgroundWorker();
            _bgExtractAllSchema.WorkerSupportsCancellation = true;
            _bgExtractAllSchema.DoWork += new DoWorkEventHandler(bgExtractAllSchema_DoWork);
            _bgExtractAllSchema.RunWorkerAsync(parms);
        }

        void bgExtractAllSchema_DoWork(object sender, DoWorkEventArgs e)
        {
            ExtractAllSchema(e.Argument as List<ExtractParameter>);
        }

        private void ExtractAllSchema(List<ExtractParameter> parms)
        {
            string zipFileName = string.Empty;
            _executionErrors = new StringBuilder();
            try
            {
                _working.Set();

                List<string> files = new List<string>();

                foreach (ExtractParameter parm in parms)
                {
                    if (_bgExtractAllSchema.CancellationPending)
                        return;

                    DBConnectionInfo connInfo = new DBConnectionInfo()
                    {
                        DataSource = parm.DataSource,
                        User = parm.UserName,
                        Password = parm.Password
                    };

                    if (!String.IsNullOrEmpty(parm.CompanyName))
                        ShowLoadingPanel("Extraindo Schema da Empresa " + parm.CompanyName);
                    else
                        ShowLoadingPanel("Extraindo Schema " + parm.DataSource + "." + parm.UserName);

                    _dbSchema = new DBSchema();

                    try
                    {
                        _dbSchema.LoadDataBaseObjects(connInfo);
                    }
                    catch (Exception ex)
                    {
                        string message = "Erro ao carregar as informações de Schema da Base de Dados."
                            + Environment.NewLine;

                        if (connInfo != null)
                            message += string.Format("DataSource= {0}, User= {1}", connInfo.DataSource, connInfo.User) + Environment.NewLine;

                        message += "Detalhes: " + ex.Message;

                        EventLog.WriteEntry("SchemaComparer.Extractor", message, EventLogEntryType.Error);

                        //TASK 12952 - Em caso de erro, passar para a próxima empresa e logar
                        _executionErrors.AppendLine(message);
                        _executionErrors.AppendLine("-------------------------------------------------");
                        continue;
                        //MessageBox.Show(message);                        
                        //return;
                    }

                    if (_bgExtractAllSchema.CancellationPending)
                        return;

                    try
                    {
                        string file = _directory + @"\" + _baseFileName + "_" + parm.DataSource + "_" + parm.UserName + ".xml";
                        _dbSchema.SaveToXmlFile(file);
                        files.Add(file);
                    }
                    catch (Exception ex)
                    {
                        string message = "Erro ao salvar as informações de Schema da Base de Dados." + Environment.NewLine + ex.Message;
                        EventLog.WriteEntry("SchemaComparer.Extractor", message, EventLogEntryType.Error);
                        MessageBox.Show(message);
                        return;
                    }
                }

                //TASK 12952 - Em caso de erro, passar para a próxima empresa e logar
                if (_executionErrors.Length > 0)
                {
                    string logFileName = _directory + @"\" + _baseFileName + "_executionErrors.log";
                    File.WriteAllText(logFileName, _executionErrors.ToString());
                    files.Add(logFileName);
                }

                if (_zipFiles && files.Count > 0)
                {
                    Ionic.Zip.ZipFile z = new Ionic.Zip.ZipFile();
                    for (int i = 0; i < files.Count; i++)
                    {
                        z.AddFile(files[i], "Schemas");

                    }

                    zipFileName = _directory + @"\Schemas_" + _dataSource + ".zip";
                    z.Save(zipFileName);

                    //remover os arquivos
                    for (int i = 0; i < files.Count; i++)
                    {
                        File.Delete(files[i]);
                    }

                    if (_executionErrors.Length == 0)
                        MessageBox.Show(string.Format("Extração do Schema realizada com sucesso. Foi gerado o arquivo {0} contendo as informações dos Schemas.", zipFileName));
                    else
                        MessageBox.Show(string.Format("Extração do Schema realizada parcialmente, verifique o arquivo de log para mais detalhes. Foi gerado o arquivo {0} contendo as informações dos Schemas.", zipFileName));
                }
                else
                {
                    if (_executionErrors.Length == 0)
                        MessageBox.Show("Extração do Schema realizada com sucesso.");
                    else
                        MessageBox.Show("Extração do Schema realizada parcialmente,verifique o arquivo de log para mais detalhes..");
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("Ocorreu um erro inesperado ao executar a extração de dados." + Environment.NewLine + ex.ToString());
                return;
            }
            finally
            {
                _working.Reset();
                HideLoadingPanel();
            }
        }

        private void ShowLoadingPanel(string text)
        {
            Dispatcher.Invoke(new Action(delegate()
                {
                    gridMain.IsEnabled = false;
                    ucLoading.Visibility = Visibility.Visible;
                    ucLoading.TxtDetail = text;
                }));
        }
        private void HideLoadingPanel()
        {
            Dispatcher.Invoke(new Action(delegate()
            {
                gridMain.IsEnabled = true;
                ucLoading.Visibility = Visibility.Collapsed;
            }));
        }

        private void lstCompanies_Selected(object sender, RoutedEventArgs e)
        {
            e.Handled = true;
        }
    }
}
